package com.wk.order.service.impl;

import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.wk.cart.entity.Cart;
import com.wk.cart.feign.CartFeign;
import com.wk.common.bean.Resp;
import com.wk.common.bean.UserInfo;
import com.wk.common.constant.RedisKey;
import com.wk.common.constant.mq.ItemExchange;
import com.wk.common.constant.mq.ItemRoutingKey;
import com.wk.oms.entity.Order;
import com.wk.oms.feign.OrderFeign;
import com.wk.oms.vo.OrderItemVO;
import com.wk.oms.vo.OrderSubmitVO;
import com.wk.order.interceptor.LoginInterceptor;
import com.wk.order.service.OrderService;
import com.wk.order.vo.OrderConfirmVO;
import com.wk.pay.vo.PayVo;
import com.wk.pms.entity.SkuInfo;
import com.wk.pms.feign.SkuInfoFeign;
import com.wk.pms.feign.SkuSaleAttrValueFeign;
import com.wk.sms.feign.SkuBoundsFeign;
import com.wk.ums.feign.MemberFeign;
import com.wk.ums.feign.MemberReceiveAddressFeign;
import com.wk.wms.entity.WareSku;
import com.wk.wms.feign.WareSkuFeign;
import com.wk.wms.vo.SkuLockVO;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private MemberReceiveAddressFeign addressFeign;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private MemberFeign memberFeign;

    @Autowired
    private CartFeign cartFeign;

    @Autowired
    private SkuInfoFeign skuInfoFeign;

    @Autowired
    private WareSkuFeign wareSkuFeign;

    @Autowired
    private SkuSaleAttrValueFeign saleAttrValueFeign;

    @Autowired
    private SkuBoundsFeign skuBoundFeign;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private OrderFeign orderFeign;

    @Autowired
    private AmqpTemplate amqpTemplate;

    /**
     * 订单确认页
     * 根据用户登录状态获取用户ID 再根据用户ID查询购物车选中的商品
     */
    @Override
    public OrderConfirmVO orderConfirm() {

        //根据登录状态获取用户ID
        Long userId = LoginInterceptor.getUserInfo().getId();
        if (userId == null) {
            return null;
        }

        OrderConfirmVO confirmVO = new OrderConfirmVO();

//---------------------------------大编排任务开始-----------------------------------------------

        //获取购物车中选中的商品信息 其他方法需要该方法的返回值
        CompletableFuture<Void> cartInfoTask = CompletableFuture.supplyAsync(() -> {
            List<Cart> carts = cartFeign.queryCheckedCartByUserId(userId).getData();
            if (CollectionUtils.isEmpty(carts)) {
                throw new RuntimeException("请勾选购物车商品");
            }
            return carts;
        }, threadPoolExecutor).thenAcceptAsync(carts -> {

            //大编排任务的子任务 需要第二个任务的返回值
            List<OrderItemVO> itemVOs = carts.stream().map(cart -> {
                Long skuId = cart.getSkuId();

                OrderItemVO orderItemVO = new OrderItemVO();
                orderItemVO.setSkuId(skuId);
                orderItemVO.setCount(cart.getCount());

                //查询sku信息 第一个小编排任务
                CompletableFuture<Void> skuTask = CompletableFuture.runAsync(() -> {
                    SkuInfo sku = skuInfoFeign.info(skuId).getData();
                    if (sku != null) {
                        orderItemVO.setTitle(sku.getSkuTitle());
                        orderItemVO.setDefaultImage(sku.getSkuDefaultImg());
                        orderItemVO.setPrice(sku.getPrice());
                        orderItemVO.setWeight(sku.getWeight());
                    }
                }, threadPoolExecutor);


                //查询库存信息 第二个小编排任务
                CompletableFuture<Void> wareTask = CompletableFuture.runAsync(() -> {
                    List<WareSku> wareSkus = wareSkuFeign.queryBySkuId(skuId).getData();
                    if (CollectionUtils.isNotEmpty(wareSkus)) {
                        orderItemVO.setStore(wareSkus.stream().anyMatch(wareSku -> wareSku.getStock() > 0));
                    }
                }, threadPoolExecutor);

                //查询销售属性 第三个小编排任务
                CompletableFuture<Void> saleAttrTask = CompletableFuture.runAsync(() -> {
                    orderItemVO.setSaleAttrValues(saleAttrValueFeign.queryBySkuId(skuId).getData());
                }, threadPoolExecutor);

                //查询营销属性 第四个小编排任务
                CompletableFuture<Void> skuBoundTask = CompletableFuture.runAsync(() -> {
                    orderItemVO.setSaleS(skuBoundFeign.querySaleVoBySkuId(skuId).getData());
                }, threadPoolExecutor);

                //四个小编排任务完成
                CompletableFuture.allOf(
                        skuTask,
                        wareTask,
                        saleAttrTask,
                        skuBoundTask
                ).join();

                return orderItemVO;
            }).collect(Collectors.toList());

            //订单商品详情
            confirmVO.setOrderItems(itemVOs);

            //生成唯一标志 一份响应页面,一份保存到Redis 防止订单重复提交 作为订单的编号保存 不需要返回值
            String orderToken = IdWorker.getIdStr();
            confirmVO.setOrderToken(orderToken);
            redisTemplate.boundValueOps(RedisKey.ORDER_TOKEN + orderToken).set(orderToken);

        },threadPoolExecutor);

//---------------------------------大编排任务结束-----------------------------------------------

        //根据用户ID查询收货地址列表 不需要返回值
        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            confirmVO.setAddressList(addressFeign.queryByUserId(userId).getData());
        }, threadPoolExecutor);

        //用户积分 不需要返回值
        CompletableFuture<Void> memberTask = CompletableFuture.runAsync(() -> {
            confirmVO.setIntegral(memberFeign.info(userId).getData().getIntegration());
        }, threadPoolExecutor);

        //四个大编排任务完成
        CompletableFuture.allOf(
                addressTask,
                cartInfoTask,
                memberTask
        ).join();

        return confirmVO;
    }

    @Override
    public void submit(OrderSubmitVO orderSubmitVO) {
        //防止订单重复提交 判断Redis中是否有token 如果有 则是第一次提交 放行并删除token 第二次提交没有token则拦截
        String orderToken = orderSubmitVO.getOrderToken();

        /*在Redis中使用LUA脚本做查询和删除，使操作具备原子性
         脚本功能：根据获取传入的key从Redis中获取值，如果和传入的值相同就删除这条数据并返回1，不一样则返回0*/
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        /*
         * execute方法参数
         *       1：Lua脚本
         *       2：Redis的key list类型 这里只有一个key 所以转为单元素的集合
         *       3：需要比较的值
         *
         * Long.class: 返回值类型
         * */
        Long flag = redisTemplate.execute(new DefaultRedisScript<>(script,Long.class), Collections.singletonList(RedisKey.ORDER_TOKEN + orderToken), orderToken);
        if (flag==0) {
            throw new RuntimeException("请勿重复提交订单");
        }

        //校验价格 总价一致则放行 否则提示用户刷新页面后重试
        BigDecimal totalPrice = orderSubmitVO.getTotalPrice();      //订单总价
        List<OrderItemVO> items = orderSubmitVO.getItems();         //订单商品列表
        if (CollectionUtils.isEmpty(items)) {
            throw new RuntimeException("请勾选购物车中的商品");
        }

        //获取实时总价
        BigDecimal currentTotalPrice = items.stream().map(item -> {
            SkuInfo sku = skuInfoFeign.info(item.getSkuId()).getData();
            if (sku != null) {
                //sku单价 * 订单商品数量
                return sku.getPrice().multiply(new BigDecimal(item.getCount()));
            }
            return new BigDecimal(0);
            //reduce：1+2+3+...
        }).reduce(BigDecimal::add).get();

        //判断实时总价和订单总价是否一致
        if (currentTotalPrice.compareTo(totalPrice) != 0) {
            throw new RuntimeException("页面已过期,请刷新后重试");
        }

        List<SkuLockVO> skuLockVOS = items.stream().map(item -> {
            SkuLockVO skuLockVO = new SkuLockVO();
            skuLockVO.setSkuId(item.getSkuId());
            skuLockVO.setCount(item.getCount());
            skuLockVO.setOrderToken(orderToken);
            return skuLockVO;
        }).collect(Collectors.toList());

        //判断库存是否锁定成功
        Resp<String> wareResp = wareSkuFeign.checkAndLockStore(skuLockVOS);
        if (wareResp.getCode() == 1) {
            throw new RuntimeException(wareResp.getMsg());
        }

        //获取用户ID
        Long userId = LoginInterceptor.getUserInfo().getId();
        orderSubmitVO.setUserId(userId);

        /*测试解锁商品库存锁定成功但出现异常未能成功创建订单的商品库存
        int i = 1/0;*/

        //下单 创建订单及订单详情
        Order order;
        try {
            //下单成功 保存订单
            order = orderFeign.saveOrder(orderSubmitVO).getData();
        } catch (Exception e) {
            e.printStackTrace();

            //订单创建失败 需要发送消息到库存服务解锁之前已锁定的对应商品库存
            amqpTemplate.convertAndSend(ItemExchange.ORDER_EXCHANGE,ItemRoutingKey.STOCK_UNLOCK,orderToken);

            throw new RuntimeException("服务器繁忙,订单创建失败");
        }

        /**
         * 发送用户ID和需要删除的商品ID到消息队列
         * 购物车服务监听队列中的消息并根据参数删除购物车中已下单的商品
         * 删除失败不影响订单的创建
         */
        Map<String,Object> cartMap = new HashMap<>();
        cartMap.put("userId",userId);
        List<Long> skuIds = items.stream().map(OrderItemVO::getSkuId).collect(Collectors.toList());
        cartMap.put("skuIds",skuIds);

        amqpTemplate.convertAndSend(ItemExchange.ORDER_EXCHANGE, ItemRoutingKey.CART_DELETE,cartMap);

        //支付
        PayVo payVo = new PayVo();
        payVo.setOut_trade_no(order.getOrderSn());      //订单编号 防止重复支付
        payVo.setTotal_amount(order.getPayAmount()==null?"100":order.getPayAmount().toString());      //支付金额
        payVo.setSubject("商城");
        payVo.setBody("支付");

        amqpTemplate.convertAndSend(ItemExchange.ORDER_EXCHANGE, ItemRoutingKey.ORDER_PAY,payVo);

    }

}
